package comm;

import java.io.IOException;
import java.io.Serializable;
import java.net.DatagramPacket;
import java.net.NetworkInterface;
import java.util.Arrays;
import java.util.Observable;
import java.util.Observer;

import constants.EUdpGroupName;
import constants.EUdpPortNum;
//import encryption.Encryption;

public class UdpCommsPackage extends Observable implements Observer{
	private UdpBroadcaster broadcaster;
	private UdpReceiver receiver;
	private Thread broadcasterThread;
	private Thread receiverThread;
	//private Encryption encryption;
	private Serializable serial;

	public UdpCommsPackage(String passphrase) throws Exception{
		setupSockets();
		//encryption = new Encryption(passphrase);
	}

	private void setupSockets() throws IOException{
		/*this sets up Receivers and Broadcasters and passes events
		 */
		//set properties
		System.setProperty("java.net.preferIPv4Stack", "true");
		if(NetworkInterfaceHandler.getInstance().getNics().size() <= 0){
			throw new IOException("No multicast network interfaces available");
		}

		//gets the first network interface
		NetworkInterface ni = NetworkInterfaceHandler.getInstance().getNics().get(0);

		//sets up the Ping receiver
		receiverThread = new Thread(
				receiver = new UdpReceiver(
						ni,
						EUdpPortNum.COMM.getPortNum(),
						EUdpGroupName.COMM_GROUP.getGroupName())
		);
		receiverThread.start();
		receiver.addObserver(this);

		//sets up the broadcaster
		broadcasterThread = new Thread (
				broadcaster = new UdpBroadcaster(
						ni,
						EUdpPortNum.COMM.getPortNum(),
						EUdpGroupName.COMM_GROUP.getGroupName())
		);
		broadcasterThread.start();


	}

	@Override
	public void update(Observable o, Object datagramPacket) {
		//decrypt, deserialize and passes the object to the observer

		if(datagramPacket != null && datagramPacket instanceof DatagramPacket){
			DatagramPacket packet = (DatagramPacket) datagramPacket;
			serial = null;
			try {
				//gets the encrypted packet and copies the live data
				//if(packet.getLength()%16==0){
					//byte[] encrypted = Arrays.copyOf(packet.getData(), packet.getLength());

					//decrypts
					//byte[] bytes = encryption.decrypt(encrypted);

					byte[] bytes = Arrays.copyOf(packet.getData(), packet.getLength());
					//deserializes
					serial = Serializer.getInstance().deserialize(bytes);

				//}
				//else {
				//	System.err.println("Improper block size packet: "+packet.getLength());
				//}
			} catch (IOException e) {
				e.printStackTrace();
			} catch (ClassNotFoundException e) {
				e.printStackTrace();
			} catch (Exception e){
				e.printStackTrace();
			}
			if (serial!=null){
				//pass the object to observers
				setChanged();
				notifyObservers(serial);
			}
		}
		else{
			if(datagramPacket == null ){
				System.err.println("Datagram packet was null");
			}
		}
	}

	public void broadcastSerializable(Serializable serial) throws Exception{
		//serialize and broadcast the event on the network interface and return true 
		//else return false

		byte[] bytes = Serializer.getInstance().serialize(serial);
		//byte[] encrypted = encryption.encrypt(bytes);
		//broadcaster.sendPacket(encrypted, EUdpPortNum.COMM.getPortNum());
		broadcaster.sendPacket(bytes, EUdpPortNum.COMM.getPortNum());

	}
	public void close(){
		//closes the sockets from the receiver and broadcaster
		broadcaster.close();
		receiver.close();
		try {
			broadcasterThread.join();
			receiverThread.join();
		} catch (InterruptedException e) {
			System.out.print("Threads interrupted in close()");
		}
	}
}
